import dis
from opcode import HAVE_ARGUMENT, opmap, opname

import functools
from psycopg2 import OperationalError
from types import CodeType
import logging
import sys
import werkzeug

from . import ustr, pycompat, wrap_values

import odoo

unsafe_eval = eval

__all__ = ['test_expr', 'safe_eval', 'const_eval']

_ALLOWED_MODULES = ['_strptime', 'math', 'time']

_UNSAFE_ATTRIBUTES = ['f_builtins', 'f_globals', 'f_locals', 'gi_frame',
                      'co_code', 'func_globals']
_POSSIBLE_OPCODES_P3 = [
    'WITH_CLEANUP_START', 'WITH_CLEANUP_FINISH',
    'FORMAT_VALUE', 'BUILD_STRING',
    'UNPACK_EX',
    'BUILD_LIST_UNPACK', 'BUILD_TUPLE_UNPACK', 'BUILD_SET_UNPACK', 'BUILD_MAP_UNPACK',
    'BUILD_TUPLE_UNPACK_WITH_CALL', 'BUILD_MAP_UNPACK_WITH_CALL',
    'GET_YIELD_FROM_ITER',
    'BINARY_MATRIX_MULTIPLY', 'INPLACE_MATRIX_MULTIPLY',
]

_CONST_OPCODES = set(opmap[x] for x in [
    # stack manipulations
    'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP', 'DUP_TOPX',
    'DUP_TOP_TWO',  # replaces DUP_TOPX in P3
    'LOAD_CONST',
    'RETURN_VALUE', # return the result of the literal/expr evaluation
    'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE', 'BUILD_SET',
    'BUILD_CONST_KEY_MAP',
    'STORE_MAP',
] if x in opmap)

_EXPR_OPCODES = _CONST_OPCODES.union(set(opmap[x] for x in [
    'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
    'UNARY_INVERT', 'BINARY_POWER', 'BINARY_MULTIPLY',
    'BINARY_DIVIDE', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE',
    'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR',
    'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR',
    'BINARY_OR', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY',
    'INPLACE_DIVIDE', 'INPLACE_REMAINDER', 'INPLACE_POWER',
    'INPLACE_LEFTSHIFT', 'INPLACE_RIGHTSHIFT', 'INPLACE_AND',
    'INPLACE_XOR','INPLACE_OR', 'STORE_SUBSCR',
    'SLICE+0', 'SLICE+1', 'SLICE+2', 'SLICE+3', 'BUILD_SLICE',
    'LIST_APPEND', 'MAP_ADD', 'SET_ADD',
    'COMPARE_OP',
] if x in opmap))

_SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
    'POP_BLOCK', 'POP_EXCEPT', # Seems to be a special-case of POP_BLOCK for P3
    'SETUP_LOOP', 'BREAK_LOOP', 'CONTINUE_LOOP',
    'MAKE_FUNCTION', 'CALL_FUNCTION',
    'EXTENDED_ARG',  # P3.6 for long jump offsets.
    'CALL_FUNCTION_EX',
    'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_VAR_KW',
    'CALL_METHOD', 'LOAD_METHOD',
    'GET_ITER', 'FOR_ITER', 'YIELD_VALUE',
    'JUMP_FORWARD', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_ABSOLUTE',
    'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
    'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'END_FINALLY',
    'RAISE_VARARGS', 'LOAD_NAME', 'STORE_NAME', 'DELETE_NAME', 'LOAD_ATTR',
    'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', 'UNPACK_SEQUENCE',
    'LOAD_GLOBAL', # Only allows access to restricted globals
] if x in opmap))

_logger = logging.getLogger(__name__)

if hasattr(dis, 'get_instructions'):
    def _get_opcodes(codeobj):
        pass

def assert_no_dunder_name(code_obj, expr):
    pass

def assert_valid_codeobj(allowed_codes, code_obj, expr):
    pass

def test_expr(expr, allowed_codes, mode="eval"):
    pass

def const_eval(expr):
    pass

def expr_eval(expr):
    pass

def _import(name, globals=None, locals=None, fromlist=None, level=-1):
    pass

_BUILTINS = {
    '__import__': _import,
 'True': True,
    'False': False,
    'None': None,
    'bytes': bytes,
    'str': str,
    'unicode': pycompat.text_type,
    'bool': bool,
    'int': int,
    'float': float,
    'enumerate': enumerate,
    'dict': dict,
    'list': list,
    'tuple': tuple,
    'map': map,
    'abs': abs,
    'min': min,
    'max': max,
    'sum': sum,
    'reduce': functools.reduce,
    'filter': filter,
    'sorted': sorted,
    'round': round,
    'len': len,
    'repr': repr,
    'set': set,
    'all': all,
    'any': any,
    'ord': ord,
    'chr': chr,
    'divmod': divmod,
    'isinstance': isinstance,
    'range': range,
    'xrange': range,
    'zip': zip,
    'Exception': Exception,
}
def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False, locals_builtins=False):
    pass

def test_python_expr(expr, mode="eval"):
    pass

